// 解析 body
import bodyParser from 'body-parser'
// 解析表单
import multipart from 'connect-multiparty'
// 解析 cookie
import cookieParser from 'cookie-parser'
import express, { Express, Request, Response, NextFunction } from 'express'
import { getMeta, ParamMeta, ParamTypeMap, DataFormat } from './decorator'
import { Result } from './common'

function initExpress(app: Express) {
  // 设置只执行一次
  if (global.IS_INIT_EXPRESS) return
  global.IS_INIT_EXPRESS = true

  // 解析 cookie
  app.use(cookieParser())
}

function getParamByName(obj: any) {
  return function (paramName?: string) {
    if (paramName && paramName.trim()) {
      try {
        return obj[paramName]
      } catch (error) {
        return undefined
      }
    } else {
      return obj
    }
  }
}

function getParams(req: Request, res: Response, next: NextFunction, paramMetas: ParamMeta[]): any[] {
  const paramMap: ParamTypeMap = {
    request: getParamByName(req),
    response: getParamByName(res),
    query: getParamByName(req.query),
    body: getParamByName(req.body),
    cookie: getParamByName(req.cookies),
    header: getParamByName(req.headers),
    params: getParamByName(req.params),
    next: () => next
  }
  // 按下标排序
  return paramMetas.sort(function (a, b) {
    return a.paramIndex - b.paramIndex
  }).map(function (paramMeta) {
    return paramMap[paramMeta.paramType](paramMeta.paramName)
  })
}

export function RegisterController(app: Express, Constructor: any): void {
  initExpress(app)
  const target = new Constructor() as Object
  const Controller = target.constructor
  const meta = getMeta(Controller)
  const baseRouter = express.Router()

  for (const methodName in meta.routes) {
    const subRouter = express.Router() as any
    const methodMeta = meta.routes[methodName]
    const method = (target as any)[methodName] as Function
    const httpMethod = (subRouter[methodMeta.httpMethod] as Function).bind(subRouter)    

    if (methodMeta.dataFormat === DataFormat.FORMDATA) {
      // 解析表单
      subRouter.use(multipart())
    } else {
      // 解析 body
      subRouter.use(bodyParser.json())
      subRouter.use(bodyParser.urlencoded({ extended: false }))
    }

    methodMeta.midwares.push(function (req: Request, res: Response, next: NextFunction) {
      const handle = method.apply(target, getParams(req, res, next, methodMeta.params))

      // 普通请求只执行异步方法
      if (handle instanceof Promise) {
        handle.then(result => {
          if (result instanceof Result) {
            res.json(result)
          } else if (methodMeta.isCustom) {
            // 自定义处理
          } else {
            next('控制器方法返回值不是 Result 类的实例')
          }
        }).catch(error => {
          next(error)
        })
      } else {
        next('控制器只执行异步方法，请使用 async await 或者方法返回 Promise')
      }
    })

    httpMethod.apply(target, [methodMeta.subUrl, ...meta.midwares.concat(methodMeta.midwares)])
    
    baseRouter.use(meta.baseUrl, subRouter)
  }

  app.use(baseRouter)
}